grammar Cymbol;

compilationUnit // √
    : (classDeclaration | methodDeclaration | varDeclaration)+ // √
    ;

classDeclaration // √
    : 'class' ID superClass? '{' classMember+ '}' ';' // √
    ;
superClass // √
    : ':' ID
    ;

classMember // √
    : varDeclaration // √
    | methodDeclaration // √
    ;

// START: method
methodDeclaration // √
    : type ID '(' formalParameters? ')' block
    ;
// END: method

formalParameters // √
    : parameter (',' parameter)*
    ;

parameter // √
    : type ID      # normalParameter
    | type ID '[]' # arrayParameter
    | type '*' ID  # pointerParameter
    ;

type
    : primitiveType
    | ID
    ;

primitiveType
    : 'float'
    | 'int'
    | 'char'
    | 'boolean'
    | 'void'
    ;

// START: block
block // √
    : '{' statement* '}' // √
    ;
// END: block

// START: var
varDeclaration // √
    : type ID ('=' expression)? ';'      # normalVarDeclaration // √
    | type ID '[]' ('=' expression)? ';' # arrayVarDeclaration // √
    | type '*' ID ('=' expression)? ';'  # pointerVarDeclaration // √
    ;
// END: var

statement // √
    : block                                                       # blockStatement // √
    | varDeclaration                                              # varDeclarationStatement // √
    | 'if' '(' expression ')' s= statement ('else' e= statement)? # ifStatement // √
    | 'return' expression? ';'                                    # returnStatement // √
    | lhs '=' expression ';'                                      # assignmentStatement // √
    | a= postfixExpression ';'                                    # postfixExpressionStatement // √
    ; // handles function calls like f(i);

lhs // √
    : unaryExpression
    ;

expressionList // √
    : expr (',' expr)*
    ;

expression // √
    : expr
    ;

expr // √
    // 算数运算
    : expr op='*' expr
    | expr op='/' expr
    | expr op='+' expr
    | expr op='-' expr

    // 逻辑运算
    | expr op='<' expr
    | expr op='>' expr
    | expr op='<=' expr
    | expr op='>=' expr

    // 匹值运算
    | expr op='!=' expr
    | expr op='==' expr
    | unaryExpression // √
    ;

unaryExpression // √
    : op= '-' unaryExpression # unaryMinusExpression // √
    | op= '!' unaryExpression # unaryNotExpression // √
    | op= '&' unaryExpression # unaryAddressExpression // √
    | '*' '(' ID '+' expr ')' # unaryArrayIndexExpression // convert *(a+i) to a[i]  // √
    | op= '*' unaryExpression # unaryDereferenceExpression // √
    | postfixExpression       # postfixExpressionUnary // √
    ;

// START: postfixExpression // √
postfixExpression // √
    : postfixExpression postfix // √
    | primary // √
    ;

postfix // √
    : '(' expressionList? ')' # functionCallPostfix // √
    | r= '[' expr ']'         # arrayIndexPostfix // convert a[i] to *(a+i)  // √
    | '.' ID                  # memberAccessPostfix // √
    | r= '->' ID              # pointerMemberAccessPostfix // convert p->x to (*p).x   // √
    ;

// END: postfixExpression

primary
    : ID
    | INT
    | FLOAT
    | CHAR
    | 'true'
    | 'false'
    | '(' expr ')'
    ;

// LEXER RULES

ID
    : LETTER (LETTER | '0' ..'9')*
    ;

fragment LETTER
    : ('a' ..'z' | 'A' ..'Z')
    ;

CHAR
    : '\'' . '\''
    ;

INT
    : '0' ..'9'+
    ;

FLOAT
    : INT '.' INT*
    | '.' INT+
    ;

WS
    : (' ' | '\r' | '\t' | '\n') -> channel(HIDDEN)
    ;

SL_COMMENT
    : '//' ~('\r' | '\n')* '\r'? '\n' -> channel(HIDDEN)
    ;